using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using DungeonCraft.Engine.Interfaces;
using DungeonCraft.Engine;
using DungeonCraft.Views;
using DungeonCraft.DataModel;
using DungeonCraft.DataModel.Tools;
using DungeonCraft.DataModel.Abilities;
using DungeonCraft.DataModel.Abilities.Guards;
using DungeonCraft.DataModel.AI;

namespace DungeonCraft
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class DungeonCraft : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        private Player player;
        private WireframeRenderer renderer;
        private PlayerView playerView;
        private Level level;
        private LevelView levelView;
        private WalkUp walkUp;
        private WalkDown walkDown;
        private WalkLeft walkLeft;
        private WalkRight walkRight;
        private WalkingGuard walkingGuard;
        private Jump jump;
        private JumpingGuard jumingGuard;
        private AbilityView abilityView;
        private List<Player> zombies;
        private ZombieAI zombieAi;
        private HitDetector hitDetector;
        private Camera camera;

        public DungeonCraft()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.IsFullScreen = false;


            Content.RootDirectory = "Content";
            
            zombies = new List<Player>();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            level = new Level(generateTestLevelGround());
            player = new Player("test", 1, level, new Location(1, 1), "Clyde");
            Player zombie = new Player("zombie", 2, level, new Location(50, 300), "zombie");
            zombies.Add(zombie);
            this.IsMouseVisible = true;
            
            HitDetector playerHitDetector = new HitDetector();
            playerHitDetector.hitables.Add(player);
            zombieAi = new ZombieAI(level, new CallbackTimer(), playerHitDetector);
            zombieAi.addZombie(zombie);
            hitDetector = new HitDetector();
            hitDetector.hitables.Add(zombie);            
            graphics.PreferredBackBufferWidth = 1920;
            graphics.PreferredBackBufferHeight = 1080;
            graphics.ApplyChanges();
            camera = new Camera(player, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height);
            base.Initialize();
        }

        private Level.GroundTypes[,] generateTestLevelGround()
        {
            Level.GroundTypes[,] groundTypes = new Level.GroundTypes[50, 50];
            for (int i = 0; i < 50; i++)
            {
                for (int j = 0; j < 50; j++)
                {
                    if (j == 10)
                        groundTypes[i, j] = Level.GroundTypes.jumpable;
                    else
                        groundTypes[i, j] = Level.GroundTypes.walkable;
                }
            }
            return groundTypes;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            renderer = new WireframeRenderer(GraphicsDevice, new SpriteManager(Content), camera);
            levelView = new LevelView(renderer, new WireframeAnimationManager());
            playerView = new PlayerView(renderer, new WireframeAnimationManager());
            abilityView = new AbilityView(renderer, new WireframeAnimationManager());

            walkUp = new WalkUp();
            walkDown = new WalkDown();
            walkLeft = new WalkLeft();
            walkRight = new WalkRight();

            jump = new Jump(new CallbackTimer());

            walkingGuard = new WalkingGuard();
            jumingGuard = new JumpingGuard();

            manaGuard = new ManaGuard();



            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {

            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.Escape))
                this.Exit();

            if (keyState.IsKeyDown(Keys.W))
                walkUp.Execute(level, player, walkingGuard, null);
            if (keyState.IsKeyDown(Keys.A))
                walkLeft.Execute(level, player, walkingGuard, null);
            if (keyState.IsKeyDown(Keys.S))
                walkDown.Execute(level, player, walkingGuard, null);
            if (keyState.IsKeyDown(Keys.D))
                walkRight.Execute(level, player, walkingGuard, null);
            if (keyState.IsKeyDown(Keys.Space))
                jump.Execute(level, player, jumingGuard, null);
            MouseState mouseState = Mouse.GetState();
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                Location mouseLocation = calculateMouseLocation(mouseState);
                MagicMissile magicMissile = new MagicMissile(new CallbackTimer(), hitDetector);
                magicMissile.Execute(level, player, manaGuard, mouseLocation);
            }

            Random rnd = new Random();

            if (Convert.ToInt32(rnd.NextDouble() * 10) == 5)
            {
                Player zombie = new Player("zombie", 3, level, new Location(Convert.ToInt32(rnd.NextDouble() * 400), Convert.ToInt32(rnd.NextDouble() * 400)), "soldier");
                hitDetector.hitables.Add(zombie);
                zombieAi.addZombie(zombie);
                zombies.Add(zombie);
            }

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        private Location calculateMouseLocation(MouseState mouseState)
        {
            return new Location(camera.calcInputX(mouseState.X), camera.calcInputY(mouseState.Y));
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {


            // TODO: Add your drawing code here                    

            renderer.Start();

            levelView.Draw(level);

            playerView.Draw(player);
            foreach (Player zombie in zombies)
            {
                playerView.Draw(zombie);
                if (zombie.CurrentAbility != null)
                    abilityView.Draw(zombie.CurrentAbility);
            }

            if (player.CurrentAbility != null)
                 abilityView.Draw(player.CurrentAbility);

            renderer.End();

            base.Draw(gameTime);
        }

        public ManaGuard manaGuard { get; set; }
    }
}
